/*Yipeng Huang and Scott Rogowski*/
/*yh2315 smr2167*/

//Given the instruction, decided whether to output a bubble to avoid a hazard.
module HazardDetectionUnit (
							clk, reset, 
							bubble, 
							instructions, waiting_on_writeback, branch_control, jump_control,
							enable_wait_for, set_wait_for 
							);

	//standard
	input clk;
	input reset;

	//////////
	/*BUBBLE*/
	//////////	
	//The tool of the hazard detection unit.  Stall whenever a hazard exists that must get resolved.
	output bubble;

	//////////
	/*INPUTS*/
	//////////
	//Check the instructions for possible hazards
	input [31:0] instructions;
	
	//From the register, what lines are waiting for a writeback before they should be used
	input [31:0] waiting_on_writeback;

	//Passed back when the branch or jump are over and the bubble can end.
	//TODO: What does this connect to?
	input branch_control;
	input jump_control;
	
	
	//////////
	/*OUTPUT*/
	//////////
	//Tell the register that we will need to wait for a writeback on that line
	output enable_wait_for;
	output [4:0] set_wait_for;	


	////////////////////
	/*MIDDLE VARIABLES*/
	////////////////////
	
	//Set which register to wait for if necessary
	reg [4:0] set_wait_for_mid;
	reg enable_wait_for_mid;

	//Start a hazard. Sent to a flipflop which will be disabled upon the jump/branch operation completing.
	//TODO: why is this in two separate flipflops?
	reg set_jmp;
	reg set_branch;
	
	//A hazard exists.  Any of these or the q from two flipflops above will trigger a bubble.
	/*
	reg add_hazard;
	reg lw_hazard;
	reg sw_hazard;
	reg addi_hazard;
	*/
	
	//Used to temporarily store which registers are needed up-to-date for an instruction
	reg [4:0] reg_0_need;
	reg [4:0] reg_1_need;
	reg reg_0_need_enable;
	reg reg_1_need_enable;	

	///////////////////////////////////////
	/*Deal with potential hazards by type*/
	///////////////////////////////////////
	always @(*) begin
	
		//Assume we are hazard free
		set_branch=0;
		set_jmp=0;
		//add_hazard=0;
		//lw_hazard=0;
		//sw_hazard=0;
		//addi_hazard=0;
		reg_0_need_enable=0;
		reg_1_need_enable=0;
		
		//Assume that no registers need to be waited on
		enable_wait_for_mid=0;
		
		//instructions[31:26] are the opcode
		case (instructions[31:26])
			//add
			6'b000000: begin  
				//wait for the destination register to be written to. Rtype
				enable_wait_for_mid = 1;
				set_wait_for_mid = instructions[15:11];	
				
				//we will need these registers for this operation
				reg_0_need_enable=1;
				reg_1_need_enable=1;
				reg_0_need = instructions[25:21];
				reg_1_need = instructions[20:16];				
				end
			
			//lw	
			6'b100011: begin
				//wait for the destination register to be written to. Itype
				enable_wait_for_mid = 1;
				set_wait_for_mid = instructions[20:16]; 
				
				//we will need this register for this operation
				reg_0_need_enable=1;
				reg_0_need = instructions[25:21];				
				
				end
			
			//sw
			6'b101011: begin 
				//will have no set wait for because we are not storing to any registers
				
				//we will need this register for this operation
				reg_0_need_enable=1;
				reg_0_need = instructions[20:16];
				
				end
				
			//addi
			6'b001000: begin  
				//wait for the destination register to be written to. Itype
				enable_wait_for_mid = 1;
				set_wait_for_mid = instructions[20:16]; 
				
				//we will need this register for this operation
				reg_0_need_enable=1;
				reg_0_need = instructions[25:21];

				end
				
			//beq
			6'b000100: begin 
				set_branch=1;
				end
				
			//jmp
			6'b000010: begin
				set_jmp=1; 
				end
				
			//default case to make case statement complete
			default: begin
				;
				end
				
			endcase
		end
	
	//Whether and what is the register that needs to be waited on
	assign enable_wait_for = enable_wait_for_mid;
	assign set_wait_for = set_wait_for_mid;

	wire [31:0] reg_0_need_decoded;
	wire [31:0] reg_1_need_decoded;
	Decoder d0 (.decoder_in(reg_0_need), .decoder_out(reg_0_need_decoded));
	Decoder d1 (.decoder_in(reg_1_need), .decoder_out(reg_1_need_decoded));

	//if these registers are in use, there is a hazard
	wire register_hazard;
	assign register_hazard = | (
							waiting_on_writeback & (
								(reg_0_need_decoded & reg_0_need_enable) |
								(reg_1_need_decoded & reg_1_need_enable)
								) 
							);

	//Whether there currently exists a jump or a branch hazard is kept track of here
	wire jmp_hazard;
	wire branch_hazard;
	FF f0 (.clk(clk), .enable(set_jmp | jump_control), .set(set_jmp), .reset(reset), .q(jmp_hazard));
	FF f1 (.clk(clk), .enable(set_branch | branch_control), .set(set_branch), . reset(reset), .q(branch_hazard));
	
	//On hazard, cause a bubble.
	//TODO: jump/branch hazards should influence PC, while register hazards should be handled by no-ops
	assign bubble = jmp_hazard | branch_hazard | register_hazard;
	
	endmodule
